﻿using System;
using System.Linq;
using COFE.Core;
using COFE.Core.EntryType;
using COFE.Core.Filter;
using COFE.Core.Utils;
using COFE.Data.Utils;

namespace COFE.Data
{
    public class DBEntryTypeDictionary : IEntryTypeDictionary
    {

        #region Constructor

        public DBEntryTypeDictionary(IEntryTypeIconExtractor iconExtractor, IEntryFilterGenerator filterGenerator)
        {
            _iconExtractor = iconExtractor;
            _filterGenerator = filterGenerator;
        }

        #endregion

        #region Methods


        private EntryTypeInfo DB2EntryType(COFEDB2 context, COFE.Data.EntryType eType)
        {
            IEntryFilter[] filters = DBFilterUtils.DB2Filter(context, eType.EntryFilters.ToArray())
                .Where(f => f is IEntryFilter).Select(f => f as IEntryFilter).ToArray();
            return new EntryTypeInfo(filters, eType.Description, eType.ContentType);
        }

        private COFE.Data.EntryType EntryType2DB(COFEDB2 context, IEntryTypeInfo eti)
        {

            var dbFilters = DBFilterUtils.Filter2DB(context, eti.Filters);
            var etypeId = eti.Filters.GetId();

            var eType = COFE.Data.EntryType.CreateEntryType(etypeId, eti.Description);
            if (!(String.IsNullOrEmpty(eti.MIMEType)))
                eType.ContentType = eti.MIMEType;

            foreach (var f in dbFilters)
                eType.EntryFilters.Add(f);

            context.AddToEntryTypes(eType);
            return eType;
        }

        private IEntryTypeInfo getEti(IFileSystemInfo entry)
        {
            var filters = FilterGenerator.GenerateFilters(entry);
            var eTypeId = filters.GetId();

            //var eti = getEti(eTypeId);
            //if (eti != null)
            //    return eti;

            IEntryTypeInfo retVal = null;
            lock (_lockObj)
                COFEDBShared.DBOperations.UpdateDatabase(
                    (context) => getEti(eTypeId),
                    (context, eti) => eti != null ? null : this.CreateEti(entry),
                    (context, eti, newEti) =>
                    {
                        if (newEti != null)
                        {
                            var eType = EntryType2DB(context, newEti);
                            context.SaveChanges();
                            retVal = newEti;
                        }
                        else
                            retVal = eti;

                        if (retVal == null)
                            throw new Exception();

                    });

            return retVal;
        }

        private IEntryTypeInfo getEti(Guid key)
        {
            return COFEDBShared.DBOperations.ReadDatabase((context) =>
                {
                    var eType = context.EntryTypes.FirstOrDefault(et => et.EntryTypeId == key);

                    if (eType != null)
                    {
                        eType.EntryFilters.Load();
                        return DB2EntryType(context, eType);
                    }
                    else
                        return null;
                });
        }

        #endregion

        #region Data

        private static Object _lockObj = new Object();
        private IEntryTypeIconExtractor _iconExtractor;
        private IEntryFilterGenerator _filterGenerator;

        #endregion

        #region Public Properties

        public IEntryTypeInfo this[Guid key] { get { return getEti(key); } }
        public IEntryTypeInfo this[IFileSystemInfo entry] { get { return getEti(entry); } }
        public IEntryTypeIconExtractor IconExtractor { get { return _iconExtractor; } private set { _iconExtractor = value; } }
        public IEntryFilterGenerator FilterGenerator { get { return _filterGenerator; } private set { _filterGenerator = value; } }


        #endregion
    }
}
